﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;

namespace YourWorld.Common.Services
{
    public class ReceiveMessageManager
    {
        private static ReceiveMessageManager instance;
        private static readonly int ChunkSize = 128;
        private static readonly int ChunkOffset = 3 * 16;
        private static readonly int MessageSize = ChunkSize - ChunkOffset;

        private Dictionary<int, Dictionary<int, byte[]>> receivedChunks;

        public Action<byte[]> MessageReceived;

        public ReceiveMessageManager()
        {
            this.receivedChunks = new Dictionary<int, Dictionary<int, byte[]>>();
        }

        public static ReceiveMessageManager Instance
        {
            get
            {
                if ( instance == null )
                    instance = new ReceiveMessageManager();

                return instance;
            }
        }

        /// <summary>
        /// Each received data packet contains:
        ///  - unique message Id  - 16bit integer
        ///  - packets count      - 16bit integer
        ///  - packet number      - 16bit integer
        ///  - data 
        /// </summary>
        /// <param name="chunk"></param>
        public void ChunkReceived( byte[] chunk )
        {
            Debug.Assert( MessageReceived != null );
            Debug.Assert( chunk.Length <= ChunkSize && chunk.Length > ChunkOffset );

            byte[] messageIdBytes = new byte[16];
            byte[] chunksCountBytes = new byte[16];
            byte[] chunkNoBytes = new byte[16];
            byte[] data = new byte[chunk.Length - ChunkOffset];

            int i;
            for ( i = 0; i < 16; i++ )
                messageIdBytes[i] = chunk[i];

            for ( i = 0; i < 16; i++ )
                chunksCountBytes[i] = chunk[i + 16];

            for ( i = 0; i < 16; i++ )
                chunkNoBytes[i] = chunk[i + 32];

            for ( i = 0; i < chunk.Length - ChunkOffset; i++ )
                data[i] = chunk[i + ChunkOffset];


            ushort messageId = BitConverter.ToUInt16( messageIdBytes, 0 );
            ushort chunksCount = BitConverter.ToUInt16( chunksCountBytes, 0 );
            ushort chunkNo = BitConverter.ToUInt16( chunkNoBytes, 0 );

            if ( this.receivedChunks.ContainsKey( messageId ) == false )
                this.receivedChunks.Add( messageId, new Dictionary<int, byte[]>() );

            Debug.Assert( this.receivedChunks[messageId].ContainsKey( chunkNo ) == false );
            this.receivedChunks[messageId].Add( chunkNo, data );

            if ( this.receivedChunks[messageId].Count != chunksCount )
                return;

            List<byte> message = new List<byte>();

            for ( i = 0; i < chunksCount; i++ )
            {
                Debug.Assert( this.receivedChunks[messageId].ContainsKey( i ) );
                message.AddRange( this.receivedChunks[messageId][i] );
            }

            this.MessageReceived( message.ToArray() );
        }
    }
}
